package com.dxy.hms.dict;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.dxy.hms.manager.DictCacheManager;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * 字典翻译实现方案：
 * （1）通过aop在接口响应数据序列化之前，用反射解析对象，逐个判读字段是否有标注需翻译的注解，然后在通过反射把字典值设到另一个字段中
 * 这种方案需要写多一个xxxText字段，因为Java中不允许在对象实例临时增加属性
 * （2）通过自定义序列化器，在接口响应数据进行JSON序列化时，通过自定义逻辑，往JSON中加入一个翻译后的xxxText字段
 * 这种方案就不需要多写一个字段，在Jackson中添加自定义字段的api是@JsonAnyGetter
 */
public interface DictTranslate {

    String DICT_FIELD_SUFFIX = "Text";

    /**
     * 在需要序列化为JSON字符串的对象中加一个用 @JsonAnyGetter 标注的方法就可以实现自定义序列化逻辑
     * 此方法有三点要求：
     * （1）访问权限字符必须是public
     * （2）返回类型必须是Map<String, Object>
     * （3）必须是无参方法
     * 此处把这个方法统一封装在一个接口中，则类实现这个接口接口，并在需要翻译的字典字段上加 @DictField 注解
     *
     * @return
     */
    @SneakyThrows
    @JsonAnyGetter
    default Map<String, Object> translate() {

        Map<String, Object> map = new HashMap<>();

        // 获取所有的有@DictField注解的字段，包括父类的，若父类和子类有同名字段，则会返回两个，子类在前，父类在后
        Field[] fields = ReflectUtil.getFields(this.getClass(),
                field -> field.isAnnotationPresent(DictField.class) || field.isAnnotationPresent(DictField2.class));

        for (Field field : fields) {
            if (field.isAnnotationPresent(DictField.class)) {
                DictField dictField = field.getAnnotation(DictField.class);
                String fieldName = field.getName();
                String dictKey = StrUtil.isNotEmpty(dictField.value())
                        ? dictField.value()
                        : StrUtil.toUnderlineCase(fieldName);
                Object fieldValue = ReflectUtil.getFieldValue(this, field);

                // 针对判断字段值为null就不执行翻译逻辑，防止父类和子类有同名字段，而在子类赋值时没给父类赋值，就会后面字典值被null覆盖
                if (fieldValue != null) {
                    map.put(fieldName + DICT_FIELD_SUFFIX, DictCacheManager.INSTANCE.getDictName(dictKey, fieldValue.toString()));
                }
            }

            if (field.isAnnotationPresent(DictField2.class)){
                DictField2 dictField2 = field.getAnnotation(DictField2.class);
                Class<? extends DictOption> dictEnumClass = dictField2.value();
                DictOption[] enumConstants = dictEnumClass.getEnumConstants();
                Object fieldValue = ReflectUtil.getFieldValue(this, field);
                if (fieldValue != null){
                    for (DictOption option : enumConstants) {
                        if (fieldValue.toString().equals(option.toCode())){
                            map.put(field.getName() + DICT_FIELD_SUFFIX, option.toText());
                        }
                        break;
                    }
                }
            }
        }

        return map;
    }
}
